package spring.security.core.jackson;

import java.util.HashSet;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.google.common.base.Strings;

public class JsonDyncSerializer {

	private static final String DYNC_INCLUDE = "DYNC_INCLUDE";
	private static final String DYNC_EXCLUDE = "DYNC_EXCLUDE";
	
	private ObjectMapper mapper = null;
	private SimpleFilterProvider filterProvider;
	private JsonAnnotationIntrospector annotationIntrospector;
	
	public JsonDyncSerializer(ObjectMapper mapper) {
		if (mapper == null) {
			this.mapper = new ObjectMapper();
		} else {
			this.mapper = new ObjectMapper(mapper.getFactory());
			this.mapper.setDateFormat(mapper.getDateFormat());
		}

		this.filterProvider = new SimpleFilterProvider();
		this.annotationIntrospector = new JsonAnnotationIntrospector();
		this.mapper.setAnnotationIntrospector(this.annotationIntrospector);
	}

	public void filter(Class<?> clazz, String[] includes, String[] excludes) {
		if (clazz != null) {
			Set<String> sets;
			String include;
			if (excludes != null && excludes.length > 0) {
				sets = new HashSet<>();
				for (String exc : excludes) {
					if (!Strings.isNullOrEmpty(exc)) {
						sets.add(exc);
					}
				}
				if (!sets.isEmpty()) {
					include = DYNC_EXCLUDE + clazz.getName();
					this.mapper.setFilterProvider(
							this.filterProvider.addFilter(include, SimpleBeanPropertyFilter.serializeAllExcept(sets)));
					this.mapper.addMixIn(clazz, JsonDyncSerializer.DynamicExclude.class);
					return;
				}
			}

			if (includes != null && includes.length > 0) {
				sets = new HashSet<>();
				for (String inc : excludes) {
					if (!Strings.isNullOrEmpty(inc)) {
						sets.add(inc);
					}
				}
				if (sets.isEmpty()) {
					return;
				}

				include = DYNC_INCLUDE + clazz.getName();
				this.mapper.setFilterProvider(
						this.filterProvider.addFilter(include, SimpleBeanPropertyFilter.filterOutAllExcept(sets)));
				this.mapper.addMixIn(clazz, JsonDyncSerializer.DynamicInclude.class);
			}

		}
	}

	public String toJson(Object object) throws JsonProcessingException {
		return this.mapper.writeValueAsString(object);
	}

	@JsonFilter(DYNC_INCLUDE)
	interface DynamicInclude {
	}

	@JsonFilter(DYNC_EXCLUDE)
	interface DynamicExclude {
	}
	
	
}
